<?php
/*
 * MiniMe - a minimalistic OStatus complient microblogging tool
 * Copyright (C) 2010, Tuomas Koski - koski.tuomas@gmail.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @author     Tuomas Koski <koski.tuomas@gmail.com>
 * @copyright  Tuomas Koski
 * @license    http://www.gnu.org/licenses/lgpl.txt
 */
class Mm_Discovery {
    
    const EMPTY_VALUE = '';
    
    private $hostMetaProtocol = 'https';

    private $domain = self::EMPTY_VALUE;
        
    private $logger = null;
    
    private $httpClient = null;
    
    private $uri = null;
    
    /**
     * @var Mm_Xrd_Element_Hostmeta
     */
    private $hostmeta = null;
    
    /**
     * @var Mm_Xrd_Element_Lrdd
     */
    private $lrdd = null;
    
    /**
     * @var Mm_Atom_Element_Feed
     */
    private $atomFeed = null;
    
    private $atomFeedUri = null;
    
    private $webfingerProfilePage = "";
    
    public function __construct($uri, Mm_Logger_Interface $logger = null, Mm_HttpClient_Interface $httpClient = null) {
        // TODO, set logger here.
        
        $this->_initHttpClient($httpClient);
        $this->_initLogger($logger);
        
        $this->detectAndSetDomainAndURI($uri);
    }
    
    private function _initHttpClient($httpClient) {
        if( $httpClient != null && $httpClient instanceof Mm_HttpClient_Interface ) {
            $this->httpClient = $httpClient;
        } else {
            $this->httpClient = new Mm_HttpClient();
        } 
    }
    
    private function _initLogger($logger) {
        if( $logger != null && $logger instanceof Mm_Logger_Interface ) {
            $this->logger = $logger;
        }
    }    
    
    /**
     * Detects if the uri is http or acct type. Will use
     * acct: as default if "@" -type of a input provided and no
     * protocol is defined in the URI.
     * 
     * Will also parse the Domain from the URI.
     * 
     * @param string $uri
     */
    private function detectAndSetDomainAndURI($uri) {
        $uri = urldecode(trim($uri));
        $this->logThis("Starting to set domain and URI using : '" . $uri . "'.");
        
        if(strstr($uri, '@') === false &&
           substr($uri, 0, 4) != 'http') 
        {
            $uri = 'http://' . $uri;
            $this->logThis("Starting to set domain and URI using : '" . $uri . "'.");            
        }
        
        if(substr($uri, 0, 5) == 'http:') {
            $domain = parse_url($uri, PHP_URL_HOST);
            $this->setDomain($domain);
            $this->setHostMetaProtocol('http');
            
        } else if(substr($uri, 0, 6) == 'https:') {
            $domain = parse_url($uri, PHP_URL_HOST);
            $this->setDomain($domain);
            $this->setHostMetaProtocol('https');
            
        } else if(substr($uri, 0, 5) == 'acct:') {
            $pieces = explode('@', $uri);
            if(isset($pieces[1])) {
                $this->setDomain($pieces[1]);
            } else {
                $this->setDomain(self::EMPTY_VALUE);
            }
            
        } else {
            $pieces = explode('@', $uri);
            if(isset($pieces[1])) {
                $this->setDomain($pieces[1]);
                $uri = "acct:" . $uri;
            } else {
                $this->setDomain(self::EMPTY_VALUE);
            }
            
        }
        $this->setUri($uri);
    }
    
    private function setUri($uri) {
        if(empty($uri)) {
            throw new Exception(__CLASS__ . " - URI cannot be set empty!");
        }
        $this->uri = $uri;
        $this->logThis("URI set to '" . $this->uri . "'.");
    }
    
    public function getUri() {
        return $this->uri;
    }
    
    private function setAtomFeed(Mm_Atom_Element_Feed $feed) {
        if(empty($feed)) {
            throw new Exception(__CLASS__ . " - AtomFeed cannot be set empty!");
        }
        $this->atomFeed = $feed;
        $this->logThis("Atom Feed set.");
    }
    
    /**
     * @return Mm_Atom_Element_Feed
     */
    public function getAtomFeed() {
        return $this->atomFeed;
    }
    
    private function setDomain($domain) {
        if(empty($domain)) {
            throw new Exception(__CLASS__ . " - Domain cannot be set empty!");
        }
        $this->domain = $domain;
        $this->logThis("Domain set to '" . $this->domain . "'.");
    }
    
    private function setAtomFeedUri($atomFeedUri) {
        if(empty($atomFeedUri)) {
            throw new Exception(__CLASS__ . " - Atom Feed URI cannot be set empty!");
        }
        $this->atomFeedUri = $atomFeedUri;
        $this->logThis("Atom Feed Uri set to '" . $this->atomFeedUri . "'.");
    }
    
    public function getAtomFeedUri() {
        if(!empty($this->atomFeedUri)) {
            return $this->atomFeedUri;
        }
        
        if( $this->lrdd != null && $this->lrdd->getUpdatesFromLink() != null) {
            $this->setAtomFeedUri($this->lrdd->getUpdatesFromLink()->href);
            return $this->atomFeedUri;
        }
        return self::EMPTY_VALUE;
    }
    
    private function setWebfingerProfilePage($webfingerProfilePage) {
        if(empty($webfingerProfilePage)) {
            throw new Exception(__CLASS__ . " - WebfingerProfilePage cannot be set empty!");
        }
        $this->webfingerProfilePage = $webfingerProfilePage;
        $this->logThis("WebfingerProfilePage set to '" . $this->webfingerProfilePage . "'.");
    }
    
    /**
     * @return Mm_Xrd_Element_Lrdd
     */
    public function getWebfingerProfilePage() {
        if(!empty($this->webfingerProfilePage)) {
            return $this->webfingerProfilePage;
        }
        
        if( $this->lrdd != null && $this->lrdd->getWebfingerProfilepageLink() != null) {
            $this->setWebfingerProfilePage($this->lrdd->getWebfingerProfilepageLink()->href);
            return $this->webfingerProfilePage;
        }
        return self::EMPTY_VALUE;
    }
    
    public function getDomain() {
        return $this->domain;
    }
    
    private function setHostMetaProtocol($protocol) {
        if(empty($protocol)) {
            throw new Exception(__CLASS__ . " - Protocol cannot be set empty!");
        }
        $this->hostMetaProtocol = $protocol;
        $this->logThis("HostMetaProtocol set to '" . $this->hostMetaProtocol . "'.");
    }
    
    public function discover() {
        if($this->discoverHostMeta()) {
            $this->discoverLrdd();
        }
        
        if( ($this->lrdd == null || $this->lrdd->getUpdatesFromLink() == null) 
            && substr($this->uri, 0, 4) == 'http') 
        {
            // No luck so far, but the uri passed is http or https so maybe
            // we find the atom from the URI
            if( $this->lrdd != null && $this->lrdd->getWebfingerProfilepageLink() != null 
                && $this->discoverWebfingerProfilePage($this->lrdd->getWebfingerProfilepageLink()) === false) 
            {
                return false;
                
            } else if(substr($this->uri, 0, 4) == 'http' 
              && $this->discoverWebfingerProfilePage($this->uri) === false ) 
            {
                return false;
            }
        }
        
        return $this->discoverAtomFeed();
    }
    
    private function lookForLinkInHeaders(array $headers) {
        foreach ($headers as $k => $v) {
            if( strstr($v, ': ') === false) {
                continue;
            }
            list($headerName, $headerValue) = explode(': ', $v);
            $headerName = trim($headerName);
            $headerValue = trim($headerValue);
            if($headerName != "Link" || strstr($headerValue, 'rel="lrdd"') === false || 
               strstr($headerValue, 'type="application/xrd+xml"') === false) {
                   continue;
            }
            $this->logThis("Cool, we found the lrdd from the profile pages headers: '" . $headerValue . "'.");
            $parsed = explode(";",  $headerValue);
            
            foreach ($parsed as $k => $v) {
                $v = trim($v);
                if(substr($v, 0, 1) == '<' && substr($v, -1) == '>') {
                    return trim($v, "<>");
                }
            }
        }
        return false;
    }
    
    private function discoverWebfingerProfilePage($url) {
        try {
            $this->logThis("Trying to parse atomFeedUri from profilePage that will be hopefully found from '" . $url ."'.");
            $rawProfilePage = $this->fetchURL($this->uri);
            
            // This is a bit of a hack.
            $possibleLrddLink = $this->lookForLinkInHeaders($rawProfilePage['headers']);
            if($possibleLrddLink !== false) {
                $this->logThis("Oh happy days! We found the Lrdd link from the heaers when calling the profile page, link: '" . $possibleLrddLink . "'.");
                if($this->fetchAndSetLrdd($possibleLrddLink)) {
                    if( $this->lrdd != null && $this->lrdd->getUpdatesFromLink() != null) {
                        $this->logThis("Oh happy days! Lrdd found! Cool.");
                        return true;
                    }
                    $this->logThis("Shit, still not luck with the LRDD.");
                }
            }
            
            $doc = new DOMDocument;
            @$doc->loadHTML($rawProfilePage['body']);
            $xpath = new DOMXPath($doc);
            $xpath_str = "//html/head/link[@type='application/atom+xml']";
            $entries = $xpath->evaluate($xpath_str);
            if(!$entries || !$entries->item(0)) {
                $this->logThis("Could not find atomFeed!");
                return false;
            }
            $this->setAtomFeedUri($entries->item(0)->getAttribute("href"));
            $this->setWebfingerProfilePage($url);
        } catch (Exception $e) {
            $this->logThis("Impossible to parse 'application/atom+xml' from url '" . $url . "'. Error: '" . $e->getMessage() . "'."); 
            return false;
        }
        return true;
    }
    
    private function discoverHostMeta() {
        if( !$this->fetchAndSetHostMeta($this->generateWellKnowHostMetaURL()) ) {
            $this->flipHostMetaProtocol();
            $this->fetchAndSetHostMeta($this->generateWellKnowHostMetaURL());
        } 
        
        if($this->hostmeta == null) {
            $this->logThis("Was not able to find hostmeta!");
            return false;
        }
        
        if($this->hostmeta->getHost()->value != $this->domain) {
            $this->logThis("The host value in the received host-meta file does not match the users! '" . $this->hostmeta->getHost()->value . "' != '" . $this->domain . "'.");
            return false;
        }
        
        return true;
    }
    
    private function discoverAtomFeed() {
        try {
            $rawAtomFeedXML = $this->fetchURL($this->getAtomFeedUri());
            
            $this->logThis("Received following AtomFeed XML from '" . $this->atomFeedUri . "': '" . $rawAtomFeedXML['body'] . "'.");
            
            $builder = new Mm_Atom_Element_Feed_Builder($rawAtomFeedXML['body']);
            $atomFeed = $builder->build();
            $this->setAtomFeed($atomFeed);

        } catch (Exception $e) {
            $this->logThis("Cannot read AtomFeed succesfully from '" . $this->atomFeedUri . "'! Error '" . $e->getMessage() . "'.");
            return false;
        }
        return true;
    }
    
    private function discoverLrdd() {
        if($this->fetchAndSetLrdd($this->hostmeta->generateUserLrddURL($this->uri))) {
            return true;
        }
        return false;
    }
    
    private function fetchAndSetLrdd($url) {
        try {
            $rawLrddXML = $this->fetchURL($url);
            
            $this->logThis("Received following LRDD XML from '" . $url . "': '" . $rawLrddXML['body'] . "'.");
            
            $builder = new Mm_Xrd_Element_Lrdd_Builder($rawLrddXML['body']);
            $lrdd = $builder->build();
            $this->setLrdd($lrdd);

        } catch (Exception $e) {
            $this->logThis("Cannot read Lrdd succesfully from '" . $url . "'! Error '" . $e->getMessage() . "'.");
            return false;
        }
        return true;
    }
    
    private function fetchAndSetHostMeta($url) {
        try {
            $rawHostMetaDataXML = $this->fetchURL($url);
            
            $this->logThis("Received following HostMeta XML from '" . $url . "': '" . $rawHostMetaDataXML['body'] . "'.");
            
            $builder = new Mm_Xrd_Element_Hostmeta_Builder($rawHostMetaDataXML['body']);
            $hostmeta = $builder->build();
            $this->setHostMeta($hostmeta);

        } catch (Exception $e) {
            $this->logThis("Cannot read HostMeta succesfully from '" . $url . "'! Error '" . $e->getMessage() . "'.");
            return false;
        }
        return true;
    }
    
    private function setHostMeta(Mm_Xrd_Element_Hostmeta $hostmeta) {
        if(empty($hostmeta)) {
            throw new Exception(__CLASS__ . " - Hostmeta cannot be set empty!");
        }
        $this->hostmeta = $hostmeta;
    }
    
    /**
     * @return Mm_Xrd_Element_Hostmeta
     */
    public function getHostMeta() {
        return $this->hostmeta;
    }
    
    private function setLrdd(Mm_Xrd_Element_Lrdd $lrdd) {
        if(empty($lrdd)) {
            throw new Exception(__CLASS__ . " - LRDD cannot be set empty!");
        }
        $this->lrdd = $lrdd;
    }
    
	/**
     * @return Mm_Xrd_Element_Lrdd
     */
    public function getLrdd() {
        return $this->lrdd;
    }
    
    protected function fetchURL($url) {
        return $this->httpClient->get($url);
    }
    
    private function logThis($logMessage, $intLevel = Mm_Logger::DEBUG) {
        //echo $logMessage . "\n";
        if($this->logger == null) {
            return;
        }
        
        $this->logger->logThis($logMessage, $intLevel);
    }
    
    public function generateWellKnowHostMetaURL() {
        return $this->hostMetaProtocol . '://' . $this->domain . '/.well-known/host-meta';
    }
    
    protected function flipHostMetaProtocol () {
        if($this->hostMetaProtocol == 'https') {
            $this->hostMetaProtocol = "http";
        } else {
            $this->hostMetaProtocol = "https";
        }
    }
    
    public function getName() {
        
        if($this->getAtomFeed()->getActivitySubject() == null &&
           $this->getAtomFeed()->getAuthor() &&
           $this->getAtomFeed()->getAuthor()->getName()) 
        {
            return $this->getAtomFeed()->getAuthor()->getName()->value;
        }
        
        
        //Let's try the poco:displayName from the activityStream
        if($this->getAtomFeed()->getActivitySubject() &&
           $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_POCO_DISPLAYNAME) &&
           $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_POCO_DISPLAYNAME)->value) 
        {
           return $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_POCO_DISPLAYNAME)->value;
        }
        // Let's try next the title
        if($this->getAtomFeed()->getActivitySubject() &&
           $this->getAtomFeed()->getActivitySubject()->getTitle() &&
           $this->getAtomFeed()->getActivitySubject()->getTitle()->value) 
        {
           return $this->getAtomFeed()->getActivitySubject()->getTitle()->value;
        }
        
        // Something crazy! Let's try the author of the first entry ....
        $entries = $this->getAtomFeed()->getEntries(); 
        if(!empty($entries) && current($entries)->getAuthor()) {
            return current($entries)->getAuthor()->getName()->value;
        }
        
        return self::EMPTY_VALUE;
    }
    
    public function getProfilePage() {
        
        if($this->getLrdd() &&
           $this->getLrdd()->getWebfingerProfilepageLink()) 
        {
            return $this->getLrdd()->getWebfingerProfilepageLink()->href;      
        }
        
        if($this->getAtomFeed()->getActivitySubject() &&
           $this->getAtomFeed()->getActivitySubject()->getAlternateLink()) 
        {
           return $this->getAtomFeed()->getActivitySubject()->getAlternateLink()->href;
        }
        
        if($this->getAtomFeed()->getAlternateLink()) {
            return $this->getAtomFeed()->getAlternateLink()->href;
        }
        
        // Something crazy! Let's try the author of the first entry ....
        $entries = $this->getAtomFeed()->getEntries(); 
        if(!empty($entries) && current($entries)->getAuthor()) {
            return current($entries)->getAuthor()->getUri()->value;
        }
        
        return self::EMPTY_VALUE;
    }
    
    public function getPhoto() {

        if($this->getAtomFeed()->getActivitySubject() &&
           $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_LINK_AVATAR_48) &&
           $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_LINK_AVATAR_48)->href) 
        {
           return $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_LINK_AVATAR_48)->href;
        }
        
        if($this->getAtomFeed()->getActivitySubject() &&
           $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_LINK_AVATAR) &&
           $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_LINK_AVATAR)->href) 
        {
           return $this->getAtomFeed()->getActivitySubject()->getChildElement(Mm_Atom_Element_ActivityStream_Subject::ELEMENT_LINK_AVATAR)->href;
        }
        
        if($this->getAtomFeed()->getChildElement(Mm_Atom_Element_Feed::ELEMENT_LOGO)) {
            return $this->getAtomFeed()->getChildElement(Mm_Atom_Element_Feed::ELEMENT_LOGO)->value;
        }
        
        // Something crazy! Let's try the author of the first entry ....
        $entries = $this->getAtomFeed()->getEntries(); 
        if(!empty($entries) && current($entries)->getAuthor() &&
           current($entries)->getAuthor()->getChildElement(Mm_Atom_Element_Author::ELEMENT_LINK_PHOTO)) 
        {
            return current($entries)->getAuthor()->getChildElement(Mm_Atom_Element_Author::ELEMENT_LINK_PHOTO)->href;
        }
        
        if(!empty($entries) &&
           current($entries)->getChildElement(Mm_Atom_Element_Entry::ELEMENT_ACTIVITY_ACTOR) &&
           current($entries)->getChildElement(Mm_Atom_Element_Entry::ELEMENT_ACTIVITY_ACTOR)->getChildElement(Mm_Atom_Element_ActivityStream_Actor::ELEMENT_LINK_AVATAR)) 
        {
            return current($entries)->getChildElement(Mm_Atom_Element_Entry::ELEMENT_ACTIVITY_ACTOR)->getChildElement(Mm_Atom_Element_ActivityStream_Actor::ELEMENT_LINK_AVATAR)->href;
        }
        
        return self::EMPTY_VALUE;
    }
    
    public function getUpdated() {
        if($this->getAtomFeed()->getUpdated()) {
            return $this->getAtomFeed()->getUpdated()->value;
        }
        
        // Something crazy! Let's try the author of the first entry ....
        $entries = $this->getAtomFeed()->getEntries(); 
        if(!empty($entries) &&
           current($entries)->getUpdated()) 
        {
            return current($entries)->getUpdated()->value;
        }
        return self::EMPTY_VALUE;
    }
    
    public function asArray() {
        
        $array['name'] = $this->getName();
        
        $array['photo'] = "http://badideaindeed.files.wordpress.com/2007/03/question-mark.jpg?w=96";
        if($this->getPhoto()) {
            $array['photo'] = $this->getPhoto();
        }
        
        $array['profile_page'] = $this->getProfilePage();
        
        $array['can_be_followed'] = false;
        if($this->getAtomFeedUri() != null &&
           $this->getAtomFeed()->getHubLink() != null ) 
        {
               $array['can_be_followed'] = true;
        }

        $array['can_comment'] = false;
        if($this->getLrdd() != null &&
           $this->getLrdd()->getMagicPublicKeyLink() &&
           ($this->getLrdd()->getSalmonLink() ||
            $this->getLrdd()->getSalmonMentionLink() ||
            $this->getLrdd()->getSalmonRepliesLink() || 
            $this->getAtomFeed()->getSalmonLink() ||
            $this->getAtomFeed()->getSalmonMentionLink() ||
            $this->getAtomFeed()->getSalmonRepliesLink()))
        {
               $array['can_comment'] = true;
        }
        
        return $array;
    }
    
    public function asText() {
        $buffer = "Discovery result for '" . $this->getUri() . "'\n";
	    $buffer .= "------------------------------------------------------\n";
	    $buffer .= " URI                  :'" . $this->getUri() . "'\n";
	    if($this->getHostMeta() != null) {
	        $buffer .= " HostMeta             :'" . $this->generateWellKnowHostMetaURL() . "'\n";
	        $buffer .= "  -> Host             :'" . $this->getHostMeta()->getHost()->value . "'\n";
	    } else {
	        $buffer .= " HostMeta             :'N/A' [Tried to find it from: '" . $this->generateWellKnowHostMetaURL() . "']\n";
	    }
	    if($this->getLrdd() != null) {
	        $buffer .= "\n LRDD                 :'" . $this->getHostMeta()->generateUserLrddURL($this->getUri()) . "'\n";
	        $buffer .= "  -> Subject          :'" . $this->getLrdd()->getSubject()->value . "'\n";
	        $buffer .= "  -> Alias            :'" . $this->getLrdd()->getAlias()->value . "'\n";
	        $salmon = ($this->getLrdd()->getSalmonLink() != null) ? $this->getLrdd()->getSalmonLink()->href : 'N/A';
	        $buffer .= "  -> Salmon           :'" . $salmon . "'\n";
	        $salmon = ($this->getLrdd()->getSalmonMentionLink() != null) ? $this->getLrdd()->getSalmonMentionLink()->href : 'N/A';
	        $buffer .= "  -> Salmon-mention   :'" . $salmon . "'\n";
	        $salmon = ($this->getLrdd()->getSalmonRepliesLink() != null) ? $this->getLrdd()->getSalmonRepliesLink()->href : 'N/A';
	        $buffer .= "  -> Salmon-replies   :'" . $salmon . "'\n";
	        $buffer .= "  -> Updates From     :'" . $this->getLrdd()->getUpdatesFromLink()->href . "'\n";
	        $buffer .= "  -> Profile Page     :'" . $this->getLrdd()->getWebfingerProfilepageLink()->href . "'\n";
	        $mpk = ($this->getLrdd()->getMagicPublicKeyLink() != null) ? $this->getLrdd()->getMagicPublicKeyLink()->href : 'N/A';
	        $buffer .= "  -> MagicPublicKey   :'" . $mpk . "'\n";
	    } else {
	        $lrdd = ($this->getHostMeta() != null) ? $this->getHostMeta()->generateUserLrddURL($this->getUri()) : 'N/A';
	        $buffer .= "\n LRDD                 :'N/A' [Tried to find it from: '" . $lrdd. "']\n";
	    }
	    $buffer .= "\n Atom Feed at         :'" . $this->getAtomFeedUri() . "'\n";
	    $buffer .= "  -> Title            :'" . $this->getAtomFeed()->getTitle()->value . "'\n";
	    $buffer .= "  -> ID               :'" . $this->getAtomFeed()->getId()->value . "'\n";
	    $buffer .= "  -> Updated          :'" . $this->getUpdated() . "'\n";
	    $buffer .= "  -> Hub              :'" . $this->getAtomFeed()->getHubLink()->href . "'\n";
	    
	    $salmon = ($this->getAtomFeed()->getSalmonLink() != null) ? $this->getAtomFeed()->getSalmonLink()->href : 'N/A';
	    $buffer .= "  -> Salmon           :'" . $salmon . "'\n";
	    $salmon = ($this->getAtomFeed()->getSalmonMentionLink() != null) ? $this->getAtomFeed()->getSalmonMentionLink()->href : 'N/A';
	    $buffer .= "  -> Salmon-mention   :'" . $salmon . "'\n";
	    $salmon = ($this->getAtomFeed()->getSalmonRepliesLink() != null) ? $this->getAtomFeed()->getSalmonRepliesLink()->href : 'N/A';
	    $buffer .= "  -> Salmon-replies   :'" . $salmon . "'\n";
	    $alternate = ($this->getAtomFeed()->getAlternateLink() != null) ? $this->getAtomFeed()->getAlternateLink()->href : 'N/A' ;
	    $buffer .= "  -> Alternate html   :'" . $alternate . "'\n";
	    
	    $buffer .= " WebfingerProfilePage :'" . $this->getWebfingerProfilePage() . "'\n";
	    $buffer .= "\n";
	    $buffer .= " Profile:\n";
	    $buffer .= "  Name                :'" . $this->getName() . "'\n";
	    $buffer .= "  Photo               :'" . $this->getPhoto() . "'\n";
	    $buffer .= "  Profile page        :'" . $this->getProfilePage() . "'\n";
        
	    return $buffer;
    } 
}